﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Kernel.Impl.Core
{
    using System;
    using System.IO;
    using Neo4Net.Kernel.Impl.Nioneo.Store;
    using NUnit.Framework;

    [TestFixture]
    public class TestJumpingIdGenerator
    {
        [Test]
        public void TestIt()
        {
            int sizePerJump = 1000;
            IIdGeneratorFactory factory = new JumpingIdGeneratorFactory(sizePerJump);
            IIdGenerator generator = factory[IdType.Node];
            for (long i = 0; i < sizePerJump / 2; i++)
            {
                Assert.That(generator.NextId, Is.EqualTo(i));
            }

            for (long i = 0; i < sizePerJump - 1; i++)
            {
                long expected = 0x100000000L - sizePerJump / 2 + i;
                if (expected >= 0xFFFFFFFFL)
                {
                    expected++;
                }
                Assert.That(generator.NextId, Is.EqualTo(expected));
            }

            for (long i = 0; i < sizePerJump; i++)
            {
                Assert.That(generator.NextId, Is.EqualTo(0x200000000L - sizePerJump / 2 + i));
            }

            for (long i = 0; i < sizePerJump; i++)
            {
                Assert.That(generator.NextId, Is.EqualTo(0x300000000L - sizePerJump / 2 + i));
            }
        }

        [Test]
        public void TestOffsettedFileChannel()
        {
            String fileName = "target/var/neostore.nodestore.db";
            AbstractNeo4NetTestCase.DeleteFileOrDirectory(fileName);
            IFileSystemAbstraction offsettedFileSystem = new JumpingFileSystemAbstraction(10);
            IIdGenerator idGenerator = new JumpingIdGeneratorFactory(10)[IdType.Node];
            using (var stream = (JumpingFileSystemAbstraction.JumpingFileStream)offsettedFileSystem.Open(fileName, FileAccess.ReadWrite))
            {
                for (int i = 0; i < 16; i++)
                {
                    WriteSomethingLikeNodeRecord(stream, idGenerator.NextId, i);
                }

                stream.Close();
            }
            using (var stream = (JumpingFileSystemAbstraction.JumpingFileStream)offsettedFileSystem.Open(fileName, FileAccess.ReadWrite))
            {
                idGenerator = new JumpingIdGeneratorFactory(10)[IdType.Node];

                for (int i = 0; i < 16; i++)
                {
                    Assert.That(ReadSomethingLikeNodeRecord(stream, idGenerator.NextId), Is.EqualTo(i));
                }

                stream.Close();
            }
        }

        private byte ReadSomethingLikeNodeRecord(JumpingFileSystemAbstraction.JumpingFileStream stream, long id)
        {
            var buffer = new byte[9];
            stream.Position = id * 9;
            stream.Read(buffer);
            BitConverter.ToInt64(buffer, 0);
            return buffer[8];
        }

        private void WriteSomethingLikeNodeRecord(JumpingFileSystemAbstraction.JumpingFileStream stream, long id, int justAByte)
        {
            stream.Position = id * 9;
            var buffer = new byte[9];
            Array.Copy(BitConverter.GetBytes(4321), buffer, 8);
            buffer[8] = (byte)justAByte;
            stream.Write(buffer);
        }
    }
}
